package com.example.blog.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.blog.base.CurrentAccount;
import com.example.blog.common.StatusEnums;
import com.example.blog.entity.Article;
import com.example.blog.entity.Favorites;
import com.example.blog.entity.SysUser;
import com.example.blog.entity.dto.article.ArticleAddOrUpdateDTO;
import com.example.blog.entity.vo.article.ArticleDetailInfoVO;
import com.example.blog.entity.vo.article.ArticleListVO;
import com.example.blog.exception.BizAssertUtils;
import com.example.blog.mapper.ArticleEsDao;
import com.example.blog.mapper.ArticleMapper;
import com.example.blog.service.ArticleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.blog.service.FavoritesService;
import com.example.blog.service.LikeService;
import com.example.blog.service.SysUserService;
import com.example.blog.util.BeanUtils;
import com.example.blog.util.CurrentUserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.QueryBuilders;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * <p>
 * 文章信息 服务实现类
 * </p>
 *
 * @author wlh
 * @since 2024-01-10
 */
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Autowired
    private ArticleEsDao articleEsDao;


    @Autowired
    private FavoritesService favoritesService;


    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    @Lazy
    private SysUserService sysUserService;



    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addOrUpdate(ArticleAddOrUpdateDTO articleAddOrUpdateDTO) {
        Article article = new Article();
        if (articleAddOrUpdateDTO.getId() != null) {
            // 修改
            article = getByIdNotNull(articleAddOrUpdateDTO.getId());
        }
        BeanUtils.copyProperties(articleAddOrUpdateDTO, article, false);
        saveOrUpdate(article);
        articleEsDao.save(article);
    }

    @Override
    public List<ArticleListVO> getByFFId(Long ffid) {
        List<Favorites> favorites = favoritesService.getByFFId(ffid);
        List<Long> articleIds = favorites.stream()
                .map(Favorites::getArticleId)
                .toList();

        if (CollUtil.isEmpty(articleIds)) {
            return new ArrayList<>();
        }
        List<Article> articles = listByIds(articleIds);

        return BeanUtils.copyList(articles, ArticleListVO.class);
    }

    @Override
    public Page<ArticleListVO> search(String value, Integer current, Integer size) {
        Page<ArticleListVO> articlePage = new Page<>(current, size);
        BoolQuery.Builder builder = new BoolQuery.Builder();
        if (StrUtil.isNotBlank(value)) {
            builder.should(QueryBuilders.matchQueryAsQuery("tags", value, null, null))
                    .should(QueryBuilders.matchQueryAsQuery("summary", value, null, null))
                    .should(QueryBuilders.matchQueryAsQuery("title", value, null, null));
            builder.minimumShouldMatch("1"); // 至少匹配一个 should 子句
        }
        builder.must(QueryBuilders.termQueryAsQuery("status", StatusEnums.PUBLISHED.getValue()+""));
        NativeQuery nativeQuery = new NativeQuery(builder.build()._toQuery());
        SearchHits<Article> hits = elasticsearchTemplate.search(nativeQuery, Article.class);
        List<ArticleListVO> list = hits.getSearchHits()
                .stream()
                .map(i -> BeanUtils.copyProperties(i.getContent(), ArticleListVO.class))
                .toList();
        articlePage.setTotal(hits.getTotalHits());
        if (list.isEmpty()) {
            articlePage.setRecords(new ArrayList<>());
            return articlePage;
        }
        Set<Long> set = list.stream()
                .map(ArticleListVO::getCreatedBy)
                .collect(Collectors.toSet());

        Map<Long, String> map = sysUserService.listByIds(set)
                .stream()
                .collect(Collectors.toMap(SysUser::getId, SysUser::getNickname));
        list.forEach(i -> i.setNickname(map.get(i.getCreatedBy())));
        articlePage.setRecords(list);
        return articlePage;
    }

    @Autowired
    @Lazy
    private LikeService likeService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ArticleDetailInfoVO viewById(Long id) {
        Article article = getByIdNotNull(id);
        ArticleDetailInfoVO detailInfoVO = BeanUtils.copyProperties(article, ArticleDetailInfoVO.class);
        if (CurrentUserUtils.isLogin()) {
            CurrentAccount user = CurrentUserUtils.currentUser();
            Long likeCount = likeService.getByUserAndArticle(user.getId(), id);
            detailInfoVO.setIsLike(likeCount > 0);
            Long favoritesCount = favoritesService.getByUserAndArticle(user.getId(), id);
            detailInfoVO.setIsFavorite(favoritesCount > 0);
        } else {
            detailInfoVO.setIsLike(false);
            detailInfoVO.setIsFavorite(false);
        }
        incrementView(article, 1);
        return detailInfoVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        Article article = getByIdNotNull(id);
        removeById(article);
        articleEsDao.delete(article);
    }


    @Override
    public Article getByIdNotNull(Serializable id) {
        Article article = getById(id);
        BizAssertUtils.notNull(article, "该数据不存在");
        return article;
    }

    @Override
    public Article incrementView(Article article, Integer num) {

        article.setViews(article.getViews() + num);
        updateById(article);
        articleEsDao.save(article);
        return article;
    }

    @Override
    public Article incrementFavorite(Article article, Integer num) {
        article.setFavorites(article.getFavorites() + num);
        updateById(article);
        articleEsDao.save(article);
        return article;
    }

    @Override
    public Article incrementLike(Article article, Integer num) {
        article.setLikes(article.getLikes() + num);
        updateById(article);
        articleEsDao.save(article);
        return article;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void audit(List<Long> articleIds, Integer status, String message) {

        List<Article> articles = listByIds(articleIds);
        for (Article article : articles) {
            article.setStatus(status);
            article.setMessage(message);
        }
        updateBatchById(articles);
        articleEsDao.saveAll(articles);

    }

    @Override
    public Page<Article> backPage(Integer current, Integer size, Integer status, String title) {

        Page<Article> articlePage = new Page<>(current, size);

        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.like(Article::getTitle, title)
                .eq(Article::getStatus, status)
                .orderByDesc(Article::getCreatedTime)
                ;

        page(articlePage, queryWrapper);

        return articlePage;
    }

    @Override
    public List<Article> getListByUser(Long userId) {
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(Article::getCreatedBy, userId)
                .eq(Article::getStatus, StatusEnums.PUBLISHED.getValue())
                ;

        return list(queryWrapper);
    }

    @Override
    public Map<String, Object> countByUser(Long userId) {

        return baseMapper.countByUser(userId);
    }

    @Override
    public List<Article> myList(Integer status) {
        CurrentAccount user = CurrentUserUtils.currentUser();
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Article::getCreatedBy, user.getId());
        if (status != -1) {
            queryWrapper.eq(Article::getStatus, status);
            return list(queryWrapper);
        } else {
            return list(queryWrapper);
        }
    }
}
